package ws

import (
	"context"
	"errors"
	"net"
	"net/http"

	"gitee.com/hasika/common-server/conn/impl_base"
	"gitee.com/hasika/common-server/conn/inet"
	"gitee.com/hasika/common-server/option"
	"golang.org/x/net/websocket"
)

type Listener struct {
	connectionCh chan *Connection
	ctx          context.Context
	cancel       context.CancelFunc
	tcpLn        net.Listener
}

func NewWebsocketListener(parent context.Context, address string, serviceOption *option.ServiceOption) (*Listener, error) {
	ctx, cancel := context.WithCancel(parent)
	ln := &Listener{
		connectionCh: make(chan *Connection, 1000),
		ctx:          ctx,
		cancel:       cancel,
	}
	tcpAddress, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}
	tcpLn, err := impl_base.ListenWithRetry(func(address string) (net.Listener, error) {
		ln, err := net.ListenTCP("tcp", tcpAddress)
		if err != nil {
			return nil, err
		}
		return ln, nil
	}, address, serviceOption.ListenRetryOption())
	if err != nil {
		return nil, err
	}
	ln.tcpLn = tcpLn
	go func() {
		defer cancel()
		err = http.Serve(tcpLn, websocket.Handler(ln.WebSocketServe))
		if err != nil {
			inet.Log.Errorf("http serve error: %s", err)
		}
	}()
	return ln, nil
}

func (w *Listener) Accept() (inet.Conn, error) {
	select {
	case <-w.ctx.Done():
		return nil, errors.New("listener closed")
	case newC := <-w.connectionCh:
		return newC, nil
	}
}

func (w *Listener) Close() {
	if w.tcpLn != nil {
		w.tcpLn.Close()
	}
	w.cancel()
	inet.Log.Infof("websocket listener stopped")
}

func (w *Listener) WebSocketServe(conn *websocket.Conn) {
	wsCon := NewWebsocketConnection(conn, w.ctx)
	w.connectionCh <- wsCon
	select {
	case <-w.ctx.Done():
		//服务关停
		return
	case <-wsCon.ctx.Done():
		//链接关停
		return
	}
}
